#include "stmflash.h"

#if STM32_FLASH_SIZE < 256
  #define STM32_SECTOR_SIZE   1024  //  扇区大小 字节
#else
  #define STM32_SECTOR_SIZE   2048
#endif

#ifdef STM32_FLASH_WREN
static uint16_t STMFLASH_BUFF[STM32_SECTOR_SIZE / 2];

#endif

/**
 * @brief   从指定地址读取一个16位数据
 * @param   faddr:读取的地址
 * @retval  读取到的16位数据
 * @note    无
 */
uint16_t STMFLASH_ReadHalfWord(uint32_t faddr)
{
  return *(__IO uint16_t*)faddr;
}

/** 
 * @biref   从指定地址读取指定长度的数据
 * @param   ReadAddr: 起始地址
 *          pBuffer: 取出数据后存放的地址
 *          NumToRead: 读取数据的长度
 * @retval  无
 * @note    无
 */
void STMFLASH_Read(uint32_t ReadAddr, uint16_t *pBuffer, uint16_t NumToRead)
{
  uint16_t i;

  for(i=0; i<NumToRead; i++)
  {
    pBuffer[i] = STMFLASH_ReadHalfWord(ReadAddr);
    ReadAddr += 2;
  }
}

#ifdef STM32_FLASH_WREN
/**
 * @biref   不检查写入flash  
 * @param   WriteAddr：写入数据地址
 *          pBuffer: 写入数据内容地址
 *          NumToWrite: 写入个数
 * @retval  无
 * @note    调用HAL_FLASH_Program进行数据写入, 使用它要进行flash解锁
 */
void STMFLASH_Write_NoCheck(uint32_t WriteAddr, uint16_t *pBuffer, uint16_t NumToWrite)
{
  uint16_t i;

  for(i=0; i<NumToWrite; i++)
  {
    HAL_FLASH_Program(FLASH_TYPEPROGRAM_HALFWORD, WriteAddr, pBuffer[i]);
    WriteAddr += 2;
  }
}

/**
 * @brief   从指定地址写入指定长度数据   
 * @param   WriteAddr: 写入数据的地址
 *          pBuffer: 写入数据的内容
 *          NumToWrite: 写入数据的长度
 * @retval  无
 * @note    先计算写入地址的偏移地址, 将该地址数据读出进行校验,然后循环写入flash
 */
void STMFLASH_Write(uint32_t WriteAddr, uint16_t *pBuffer, uint16_t NumToWrite)
{
  uint16_t secoff;      // 扇区内偏移地址
  uint16_t secremain;   // 扇区内剩余空间
  uint16_t i;
  uint32_t secpos;      // 扇区地址
  uint32_t offaddr;     // 整体偏移地址

  // 非法地址
  if(WriteAddr < FLASH_BASE || (WriteAddr >= (FLASH_BASE + 1024 * STM32_FLASH_SIZE)))
    return;

  HAL_FLASH_Unlock();       // 解锁

  offaddr = WriteAddr - FLASH_BASE;             // 得到整体偏移地址
  secpos = offaddr / STM32_SECTOR_SIZE;         // 扇区地址
  secoff = (offaddr % STM32_SECTOR_SIZE) / 2;   // 扇区内偏移地址(2字节为基本单位)
  secremain = STM32_SECTOR_SIZE / 2 - secoff;   // 扇区内剩余内存大小
  // 如果在扇区范围内   一般情况下都会比扇区大
  if(NumToWrite <= secremain)
    secremain = NumToWrite;

  while(1)
  {
    // 读出整个扇区内容至STMFLASH_BUFF   将读出的数据进行校验
    STMFLASH_Read(secpos * STM32_SECTOR_SIZE + FLASH_BASE, STMFLASH_BUFF, STM32_SECTOR_SIZE / 2);

    for(i=0; i<secremain; i++)
    {
      if(STMFLASH_BUFF[secoff + i] != 0xFFFF)     // 需要擦除
        break;
    }

    if(i < secremain)
    {
      FLASH_PageErase(secpos * STM32_SECTOR_SIZE + FLASH_BASE);     // 擦除该扇区
      for(i=0; i<secremain; i++)
      {
        STMFLASH_BUFF[secoff + i] = pBuffer[i];         // 复制
      }
      // 写入整个扇区
      STMFLASH_Write_NoCheck(secpos * STM32_SECTOR_SIZE + FLASH_BASE, STMFLASH_BUFF, STM32_SECTOR_SIZE / 2);      
    }
    else
    {
      STMFLASH_Write_NoCheck(WriteAddr, pBuffer, secremain);      // 直接写入扇区剩余空间
    }

    if(NumToWrite == secremain)     // 写入结束
      break;
    else
    {
      secpos++;                     // 扇区地址+1
      secoff = 0;                   // 偏移量置1
      pBuffer += secremain;         // 指针偏移
      WriteAddr += secremain;       // 写入地址偏移
      NumToWrite -= secremain;      // 减去已写入的数据
      if(NumToWrite > (STM32_SECTOR_SIZE / 2))    // 下一个扇区还是写不完
        secremain = STM32_SECTOR_SIZE / 2;
      else
        secremain = NumToWrite;
    }
  }

  HAL_FLASH_Lock();       
}
#endif

/* flash写数据测试 */
void Test_Write(uint32_t WriteAddr, uint16_t Write_Data)
{
  STMFLASH_Write(WriteAddr, &Write_Data, 1);    // 写入一个字的数据
}
